/*
 *  Announcer.c
 *  xgps-plugin
 *
 *  Created by Miguel Arroz on 10/08/18.
 *  Copyright 2010 Miguel Arroz. All rights reserved.
 *
 */

#include "Announcer.h"
#include <pthread.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "Broadcaster.h"

#define SERVICE_REG_TYPE "_xgps._udp"
#define VOULEZ_VOUS_COUCHER_AVEC_MOI_PORT 38713
#define VVCAM_PAYLOAD_INIT "VVCAM|"

pthread_t announcerThread;
DNSServiceRef serviceRef;
int announcerSocket;
int VVCAMsocket;

void startAnnouncer( void ) {
	int result = pthread_create( &announcerThread, NULL, startAnnouncerHelper, NULL );
	
	if( result != 0 ) {
		char buffer[1024];
		sprintf( buffer, "Creation of the accouncer thread failed with error %d.", result );
		xGPSDebug( buffer );
	}
}

void *startAnnouncerHelper( void *ptr ) {
	initVVCAMSocket();
	DNSServiceErrorType error = DNSServiceRegister( &serviceRef, 0, 0, NULL, SERVICE_REG_TYPE, NULL, NULL,
												   htons( VOULEZ_VOUS_COUCHER_AVEC_MOI_PORT ), 0, NULL, serviceRegisterCallback, NULL );
	
	if( error != kDNSServiceErr_NoError ) {
		char buffer[1024];
		sprintf( buffer, "DNSServiceRegister call failed with error %d.", error );
		xGPSDebug( buffer );
		return NULL;
	}
	
	announcerSocket = DNSServiceRefSockFD( serviceRef );
	
	if( announcerSocket == -1 ) {
		xGPSDebug( "Could not create announcer socket." );
		return NULL;
	}
	
	while( 1 ) {
		fd_set socketsSet;
		
		FD_ZERO( &socketsSet );
		FD_SET( announcerSocket, &socketsSet );
		FD_SET( VVCAMsocket, &socketsSet );
		
		int activeSockets = select( FD_SETSIZE, &socketsSet, NULL, NULL, NULL );
		
		if( activeSockets > 0 ) {
			if( FD_ISSET( announcerSocket, &socketsSet ) ) {
				error = DNSServiceProcessResult( serviceRef );
				if( error != kDNSServiceErr_NoError ) {
					char buffer[1024];
					sprintf( buffer, "DNSServiceProcessResult call failed with error %d.", error );
					xGPSDebug( buffer );
				}
			}
			
			if( FD_ISSET( VVCAMsocket, &socketsSet ) ) {
				//xGPSDebug("Got message from VVCAMsocket");
				handleVVCAMMessage();
			}
		}
	}
	
	return NULL;
}

void handleVVCAMMessage() {
	char bytes[1024];
	struct sockaddr_storage address;
	socklen_t addressSize = sizeof( address );
	bzero( &address, addressSize );
	int byteCount = recvfrom( VVCAMsocket, bytes, sizeof( bytes ), 0, (struct sockaddr *) &address, &addressSize );
	
	if( byteCount == -1 ) {
		char buffer[1024];
		sprintf( buffer, "VVCAM recvfrom failed with error %d.", errno );
		xGPSDebug( buffer );
		return;
	}
	
	if( strncmp( bytes, VVCAM_PAYLOAD_INIT, strlen( VVCAM_PAYLOAD_INIT ) ) == 0 ) {
		char *randNumberStr = bytes;
		randNumberStr += strlen( VVCAM_PAYLOAD_INIT );
		long randNumber = atol( randNumberStr );
		tuneBroadcaster( randNumber, address, addressSize );
	}
}

void initVVCAMSocket() {
	struct sockaddr_in6 addr;
	memset(&addr, 0, sizeof(addr));
	addr.sin6_len = sizeof(addr);
	addr.sin6_family = AF_INET6;
	addr.sin6_port = htons( VOULEZ_VOUS_COUCHER_AVEC_MOI_PORT );
	addr.sin6_addr = in6addr_any;
	
	VVCAMsocket = socket( AF_INET6, SOCK_DGRAM, 0 );
	
	if( VVCAMsocket == -1 ) {
		xGPSDebug( "Could not create VVCAMsocket" );
		return;
	}
	
	int result = bind( VVCAMsocket, (struct sockaddr *) &addr, sizeof (addr) );
	
	if( result != 0 ) {
		char buffer[1024];
		sprintf( buffer, "Binding of the VVCAMsocket socket failed with error %d.", errno );
		xGPSDebug( buffer );
		return;
	}
	
	xGPSDebug( "VVCAMsocket established." );
}

void serviceRegisterCallback( DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErrorType errorCode, 
	const char *name, const char *regtype, const char *domain, void *context ) {
	
	if( errorCode != kDNSServiceErr_NoError ) {
		char buffer[1024];
		sprintf( buffer, "serviceRegisterCallback got error %d.", errorCode );
		xGPSDebug( buffer );
		return;
	}
	
	char* buffer = malloc( 1024 + strlen( name ) + strlen( regtype ) + strlen( domain ) );
	sprintf( buffer, "Bonjour service registered with name %s, regtype %s on domain %s", name, regtype, domain );
	xGPSDebug( buffer );
	free( buffer );
}